/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.web.gwt.basics.client.widget.richtext;

import com.genia.toolbox.web.gwt.basics.client.CssNames;
import com.genia.toolbox.web.gwt.basics.client.GwtHelper;
import com.genia.toolbox.web.gwt.basics.client.Images;
import com.genia.toolbox.web.gwt.basics.client.i18n.GwtI18nMessage;
import com.genia.toolbox.web.gwt.basics.client.message.Messages;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * A sample toolbar for use with {@link RichTextArea}. It provides a simple UI
 * for all rich text formatting, dynamically displayed only for the available
 * functionality.
 */

public class RichTextToolBar
    extends Composite
{
  /**
   * We use an inner EventListener class to avoid exposing event methods on the
   * RichTextToolbar itself.
   */
  private class EventListener
      implements ClickListener, ChangeListener, KeyboardListener
  {

    /**
     * method called when the content change.
     * 
     * @param sender
     *          event on change.
     */
    public void onChange(final Widget sender)
    {
      if (sender == backColors) {
        basic.setBackColor(backColors.getValue(backColors.getSelectedIndex()));
        backColors.setSelectedIndex(0);
      }
      else if (sender == foreColors) {
        basic.setForeColor(foreColors.getValue(foreColors.getSelectedIndex()));
        foreColors.setSelectedIndex(0);
      }
      else if (sender == fonts) {
        basic.setFontName(fonts.getValue(fonts.getSelectedIndex()));
        fonts.setSelectedIndex(0);
      }
      else if (sender == fontSizes) {
        basic.setFontSize(FONT_SIZES_CONSTANTS[fontSizes.getSelectedIndex() - 1]);
        fontSizes.setSelectedIndex(0);
      }

      if (sender == richText) {
        updateStatus();
      }
    }



    /**
     * method called when the user click on the toolbar.
     * 
     * @param sender
     *          event on click.
     */
    public void onClick(final Widget sender)
    {
      if (sender == bold) {
        basic.toggleBold();
      }
      else if (sender == italic) {
        basic.toggleItalic();
      }
      else if (sender == underline) {
        basic.toggleUnderline();
      }
      else if (sender == indent) {
        extended.rightIndent();
      }
      else if (sender == outdent) {
        extended.leftIndent();
      }
      else if (sender == justifyLeft) {
        basic.setJustification(RichTextArea.Justification.LEFT);
      }
      else if (sender == justifyCenter) {
        basic.setJustification(RichTextArea.Justification.CENTER);
      }
      else if (sender == justifyRight) {
        basic.setJustification(RichTextArea.Justification.RIGHT);
      }
      else if (sender == createLink) {
        final String url = Window.prompt("Enter a link URL:", "http://");
        if (url != null) {
          extended.createLink(url);
        }
      }
      else if (sender == removeLink) {
        extended.removeLink();
      }
      else if (sender == hr) {
        extended.insertHorizontalRule();
      }
      else if (sender == ol) {
        extended.insertOrderedList();
      }
      else if (sender == ul) {
        extended.insertUnorderedList();
      }
      else if (sender == removeFormat) {
        extended.removeFormat();
      }
      else if (sender == richText) {
        // We use the RichTextArea's onKeyUp event to update the toolbar status.
        // This will catch any cases where the user moves the cursur using the
        // keyboard, or uses one of the browser's built-in keyboard shortcuts.
        updateStatus();
      }
    }



    /**
     * method called when the user push a key.
     * 
     * @param sender
     *          the widget .
     * @param keyCode
     *          the key.
     * @param modifiers
     *          is of type integer.
     */
    public void onKeyDown(final Widget sender, final char keyCode, final int modifiers)
    {
    }



    /**
     * method called when the user press a key.
     * 
     * @param sender
     *          the widget .
     * @param keyCode
     *          the key.
     * @param modifiers
     *          is of type integer.
     */
    public void onKeyPress(final Widget sender, final char keyCode, final int modifiers)
    {
    }



    /**
     * method called when the user release a key.
     * 
     * @param sender
     *          the widget .
     * @param keyCode
     *          the key.
     * @param modifiers
     *          is of type integer.
     */
    public void onKeyUp(final Widget sender, final char keyCode, final int modifiers)
    {
      if (sender == richText) {
        // We use the RichTextArea's onKeyUp event to update the toolbar status.
        // This will catch any cases where the user moves the cursur using the
        // keyboard, or uses one of the browser's built-in keyboard shortcuts.
        updateStatus();
      }
    }

  }

  /**
   * a table of font size.
   */
  private static final RichTextArea.FontSize[] FONT_SIZES_CONSTANTS = new RichTextArea.FontSize[] { RichTextArea.FontSize.XX_SMALL, RichTextArea.FontSize.X_SMALL, RichTextArea.FontSize.SMALL,
      RichTextArea.FontSize.MEDIUM, RichTextArea.FontSize.LARGE, RichTextArea.FontSize.X_LARGE, RichTextArea.FontSize.XX_LARGE };

  /**
   * the listener to set for widget.
   */
  private final transient EventListener listener = new EventListener();

  /**
   * RichTextArea widget.
   */
  private final transient RichTextArea richText;

  /**
   * get the interface BasicFormatter.
   */
  private final transient RichTextArea.BasicFormatter basic;

  /**
   * interface ExtendedFormatter.
   */
  private final transient RichTextArea.ExtendedFormatter extended;

  /**
   * vertical panel.
   */
  private final transient VerticalPanel outer = new VerticalPanel();

  /**
   * horizontal panel on top of resulting widget.
   */
  private final transient HorizontalPanel topPanel = new HorizontalPanel();

  /**
   * horizontal panel on bottom of resulting widget.
   */
  private final transient HorizontalPanel bottomPanel = new HorizontalPanel();

  /**
   * bold button.
   */
  private transient ToggleButton bold;

  /**
   * italic button.
   */
  private transient ToggleButton italic;

  /**
   * underline button.
   */
  private transient ToggleButton underline;

  /**
   * indent button.
   */
  private transient PushButton indent;

  /**
   * outdent button.
   */
  private transient PushButton outdent;

  /**
   * justifyLeft button.
   */
  private transient PushButton justifyLeft;

  /**
   * justify center.
   */
  private transient PushButton justifyCenter;

  /**
   * justify right.
   */
  private transient PushButton justifyRight;

  /**
   * hr button.
   */
  private transient PushButton hr;

  /**
   * ol button.
   */
  private transient PushButton ol;

  /**
   * ul button.
   */
  private transient PushButton ul;

  /**
   * create link button.
   */
  private transient PushButton createLink;

  /**
   * remove link button.
   */
  private transient PushButton removeLink;

  /**
   * remove format button.
   */
  private transient PushButton removeFormat;

  /**
   * list of backColors.
   */
  private transient ListBox backColors;

  /**
   * list of foreColors.
   */
  private transient ListBox foreColors;

  /**
   * list of fonts.
   */
  private transient ListBox fonts;

  /**
   * list fontSizes.
   */
  private transient ListBox fontSizes;



  /**
   * Creates a new toolbar that drives the given rich text area.
   * 
   * @param richText
   *          the rich text area to be controlled
   */
  public RichTextToolBar(final RichTextArea richText)
  {
    this.richText = richText;
    this.basic = richText.getBasicFormatter();
    this.extended = richText.getExtendedFormatter();

    outer.add(topPanel);
    outer.add(bottomPanel);

    topPanel.setWidth("100%");
    bottomPanel.setWidth("100%");

    initWidget(outer);
    setStyleName(CssNames.RICHTEXT_TOOLBAR_STYLE_NAME);

    if (basic != null) {
      topPanel.add(bold = createToggleButton(Images.IMAGES.bold(), Messages.BOLD_MESSAGE));
      topPanel.add(italic = createToggleButton(Images.IMAGES.italic(), Messages.ITALIC_MESSAGE));
      topPanel.add(underline = createToggleButton(Images.IMAGES.underline(), Messages.UNDERLINE_MESSAGE));
      topPanel.add(justifyLeft = createPushButton(Images.IMAGES.justifyLeft(), Messages.JUSTIFYLEFT_MESSAGE));
      topPanel.add(justifyCenter = createPushButton(Images.IMAGES.justifyCenter(), Messages.JUSTIFYCENTER_MESSAGE));
      topPanel.add(justifyRight = createPushButton(Images.IMAGES.justifyRight(), Messages.JUSTIFYRIGHT_MESSAGE));
    }

    if (extended != null) {

      topPanel.add(indent = createPushButton(Images.IMAGES.indent(), Messages.INDENT_MESSAGE));
      topPanel.add(outdent = createPushButton(Images.IMAGES.outdent(), Messages.OUTDENT_MESSAGE));
      topPanel.add(hr = createPushButton(Images.IMAGES.hr(), Messages.HR_MESSAGE));
      topPanel.add(ol = createPushButton(Images.IMAGES.ol(), Messages.OL_MESSAGE));
      topPanel.add(ul = createPushButton(Images.IMAGES.ul(), Messages.UL_MESSAGE));

      topPanel.add(createLink = createPushButton(Images.IMAGES.createLink(), Messages.CREATELINK_MESSAGE));
      topPanel.add(removeLink = createPushButton(Images.IMAGES.removeLink(), Messages.REMOVELINK_MESSAGE));
      topPanel.add(removeFormat = createPushButton(Images.IMAGES.removeFormat(), Messages.REMOVEFORMAT_MESSAGE));
    }

    if (basic != null) {
      bottomPanel.add(backColors = createColorList(Messages.BACKGROUND_MESSAGE));
      bottomPanel.add(foreColors = createColorList(Messages.FOREGROUND_MESSAGE));
      bottomPanel.add(fonts = createFontList());
      bottomPanel.add(fontSizes = createFontSizes());

      // We only use these listeners for updating status, so don't hook them up
      // unless at least basic editing is supported.
      richText.addKeyboardListener(listener);
      richText.addClickListener(listener);
    }
  }



  /**
   * create a color list with the given caption.
   * 
   * @param caption
   *          title of the listBox.
   * @return a listbox implemented with colorList.
   */
  private ListBox createColorList(final GwtI18nMessage caption)
  {
    final ListBox lb = new ListBox();
    lb.addChangeListener(listener);
    lb.setVisibleItemCount(1);

    GwtHelper.addToListBox(lb, caption, "");
    GwtHelper.addToListBox(lb, Messages.WHITE_MESSAGE, "white");
    GwtHelper.addToListBox(lb, Messages.BLACK_MESSAGE, "black");
    GwtHelper.addToListBox(lb, Messages.RED_MESSAGE, "red");
    GwtHelper.addToListBox(lb, Messages.GREEN_MESSAGE, "green");
    GwtHelper.addToListBox(lb, Messages.YELLOW_MESSAGE, "yellow");
    GwtHelper.addToListBox(lb, Messages.BLUE_MESSAGE, "blue");

    return lb;
  }



  /**
   * create a font list.
   * 
   * @return a listbox implemented with fontList.
   */
  private ListBox createFontList()
  {
    final ListBox lb = new ListBox();
    lb.addChangeListener(listener);
    lb.setVisibleItemCount(1);

    GwtHelper.addToListBox(lb, Messages.FONT_MESSAGE, "font");
    GwtHelper.addToListBox(lb, Messages.NORMAL_MESSAGE, "normal");

    GwtHelper.addToListBox(lb, Messages.TIMESNEWROMAN_MESSAGE, "times new roman");
    GwtHelper.addToListBox(lb, Messages.ARIAL_MESSAGE, "arial");
    GwtHelper.addToListBox(lb, Messages.COURIERNEW_MESSAGE, "courier new");
    GwtHelper.addToListBox(lb, Messages.GEORGIA_MESSAGE, "georgia");
    GwtHelper.addToListBox(lb, Messages.TREBUCHET_MESSAGE, "trebuchet");
    GwtHelper.addToListBox(lb, Messages.VERDANA_MESSAGE, "verdana");

    return lb;
  }



  /**
   * create a list of font sizes.
   * 
   * @return a listbox implemented with fontSizeList.
   */
  private ListBox createFontSizes()
  {
    final ListBox lb = new ListBox();
    lb.addChangeListener(listener);
    lb.setVisibleItemCount(1);

    GwtHelper.addToListBox(lb, Messages.SIZE_MESSAGE, "size");
    GwtHelper.addToListBox(lb, Messages.XXSMALL_MESSAGE, "xx-small");
    GwtHelper.addToListBox(lb, Messages.XSMALL_MESSAGE, "x-small");
    GwtHelper.addToListBox(lb, Messages.SMALL_MESSAGE, "small");
    GwtHelper.addToListBox(lb, Messages.MEDIUM_MESSAGE, "medium");
    GwtHelper.addToListBox(lb, Messages.LARGE_MESSAGE, "large");
    GwtHelper.addToListBox(lb, Messages.XLARGE_MESSAGE, "x-large");
    GwtHelper.addToListBox(lb, Messages.XXLARGE_MESSAGE, "xx-large");

    return lb;
  }



  /**
   * create a push button.
   * 
   * @param img
   *          the image to set on the PushButton.
   * @param tip
   *          the tooltip Text.
   * @return a pushButton.
   */
  private PushButton createPushButton(final AbstractImagePrototype img, final GwtI18nMessage tip)
  {
    final PushButton pb = new PushButton(img.createImage());
    pb.addClickListener(listener);
    GwtHelper.fillTitle(pb, tip);
    return pb;
  }



  /**
   * create a toggle button.
   * 
   * @param img
   *          the image to set on the PushButton.
   * @param tip
   *          the tooltip Text.
   * @return a ToggleButton.
   */
  private ToggleButton createToggleButton(final AbstractImagePrototype img, final GwtI18nMessage tip)
  {
    final ToggleButton tb = new ToggleButton(img.createImage());
    tb.addClickListener(listener);
    GwtHelper.fillTitle(tb, tip);
    return tb;
  }



  /**
   * Updates the status of all the stateful buttons.
   */
  private void updateStatus()
  {
    if (basic != null) {
      bold.setDown(basic.isBold());
      italic.setDown(basic.isItalic());
      underline.setDown(basic.isUnderlined());

    }
  }

}
